#include "utils.h"

#include <fstream>
#include <vector>
#include <cmath>
#include <iostream>
#include <sys/stat.h>
using namespace std;

size_t shape2size(vector<int> shape){
    size_t s=1;
    for(int i = 0; i<shape.size(); i++){
        s *= shape[i];
    }
    return s;
}

inline bool file_exists(const std::string& name) {
  struct stat buffer;   
  return (stat(name.c_str(), &buffer) == 0); 
}

void read_blob(string file, size_t n, vector<float>& out){
    if(!file_exists(file)){
        cout << "file do not exists: " << file << endl;
        throw 1;
    }
    ifstream inFile(file, ios::in|ios::binary);
    size_t bytes = n * sizeof(float);
    char* p = new char[bytes];
    inFile.read(p, bytes);
    inFile.close();
    out.resize(n);
    out.assign((float*)p, (float*)p+n);
}

void pad(vector<float>& input, vector<int> input_shape, int pad_top, int pad_bottom, int pad_left, int pad_right, vector<float>& output){
    int ic = input_shape[0];
    int ih = input_shape[1];
    int iw = input_shape[2];
    
    int oc = ic;
    int oh = ih + pad_top + pad_bottom;
    int ow = iw + pad_left + pad_right;
    size_t out_size = oc*oh*ow;
    output.resize(out_size);
    fill(output.begin(), output.end(), 0);

    for(int oc_i=0; oc_i < oc; oc_i++){
        for(int oh_i=pad_top; oh_i < oh-pad_bottom; oh_i++){
            for(int ow_i=pad_left; ow_i < ow-pad_right; ow_i++){
                output.at(oc_i*oh*ow + oh_i*ow + ow_i) = input.at(oc_i*ih*iw + (oh_i-pad_top)*iw + (ow_i-pad_left));
            }
        }
    }
}

bool vec_equal(vector<float> a, vector<float> b){
    if(a.size() != b.size()){
        cout << "vec_equal size does not match" << endl;
        return false;
    }
    for(int i=0; i<a.size(); i++){
        if(abs(a[i] - b[i]) > 1e-6){
            return false;
        }
    }
    return true;
}

bool check_float_bits(){
    int float_bits = sizeof(float) * CHAR_BIT;
    if(float_bits != 32){
        cout << "check float fails, bits = " << float_bits << endl;
        throw 1;
    }
    return true;
}


float im2col_get_pixel(vector<float>& im, int height, int width, int channels,
                        int row, int col, int channel, int pad){
    row -= pad;
    col -= pad;

    if (row < 0 || col < 0 || row >= height || col >= width){
        return 0;
    }else{
        return im[col + width*(row + height*channel)];
    }
}


/*
data_col: size = [c*ks*ks, height_col, width_col]
*/
void im2col(vector<float>& data_im, int channels, int height, int width, int ksize, int stride, int pad, vector<float>& data_col){
    int height_col = (height + 2*pad - ksize) / stride + 1;
    int width_col = (width + 2*pad - ksize) / stride + 1;
    
    data_col.resize(height_col * width_col * channels * ksize * ksize);
    fill(data_col.begin(), data_col.end(), 0);

    int channels_col = channels * ksize * ksize;
    int c,h,w;
    for (c = 0; c < channels_col; ++c) {
        int w_offset = c % ksize;
        int h_offset = (c / ksize) % ksize;
        int c_im = c / ksize / ksize;
        for (h = 0; h < height_col; ++h) {
            for (w = 0; w < width_col; ++w) {
                int im_row = h_offset + h * stride;
                int im_col = w_offset + w * stride;
                int col_index = (c * height_col + h) * width_col + w;
                data_col[col_index] = im2col_get_pixel(data_im, height, width, channels,
                        im_row, im_col, c_im, pad);
            }
        }
    }
}
